/******************************************************************************
	Cobalt - utility libraries for building XNA games
	Copyright (C) 2008 Philip Djonov

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
	for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the
	
		Free Software Foundation, Inc.
		51 Franklin Street, Fifth Floor
		Boston, MA  02110-1301, USA.
******************************************************************************/

using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Cobalt.Framework.Graphics
{
	public interface ITextHelper
	{
		/// <summary>
		/// Check if the string pointed to by <c>s</c> is a formatting
		/// sequence, and handles it if it is.
		/// </summary>
		/// <param name="s">The string to check.</param>
		/// <param name="count">The maximum number of characters to check.</param>
		/// <param name="format">
		/// The formatting stack to record format info on,
		/// or <c>null</c> if such info is not currently required.
		/// </param>
		/// <returns>
		/// The length of the formatting sequence in <c>char</c>s,
		/// or zero if s did not point to a formatting sequence.
		/// </returns>
		/// <remarks>
		/// If <c>format</c> is <c>null</c>, only the length of the sequence is
		/// required and any extra parsing work can be skipped. Otherwise any formatting
		/// marks should be processed and pushed onto the stack.
		/// </remarks>
		unsafe int ProcessFormattingSequence( char* s, int count, FormattingStack format );

		/// <summary>
		/// Returns the ideal point size for a given textScale.
		/// </summary>
		/// <param name="textScale"></param>
		/// <returns></returns>
		float GetPointSize( float textScale );
		/// <summary>
		/// Returns a scaling value that causes text rendered with a given
		/// font point to be drawn at the correct size.
		/// </summary>
		/// <param name="textScale"></param>
		/// <param name="pointSize"></param>
		/// <returns></returns>
		Vector2 GetGlyphScale( float textScale, float pointSize );
	}

	public sealed class TextHelper : ITextHelper
	{
		private int targetHeight;
		private float pointsPerPixel;
		private float screenHeightPerFontScale;
		private float dpiX, dpiY;

		/// <summary>
		/// Initializes a TextHelper.
		/// </summary>
		/// <param name="lineHeighScale">
		/// The height of one line of text, expressed as a fraction
		/// of the total screen height (for example, a value of 0.1F
		/// will cause a font rendered at a scale of 1.0F to cover one
		/// tenth of the draw target's height).
		/// </param>
		/// <param name="drawTargetHeight">
		/// The height of the buffer you wish to draw text into,
		/// in pixels.
		/// </param>
		/// <param name="drawTargetDpi">
		/// The DPI of the draw buffer.
		/// </param>
		public TextHelper( float virtualScreenLineHeight, int drawTargetHeight, float drawTargetDpi )
		{
			Reset( virtualScreenLineHeight, drawTargetHeight, drawTargetDpi );
		}

		/// <summary>
		/// Initializes a TextHelper.
		/// </summary>
		/// <param name="lineHeighScale">
		/// The height of one line of text, expressed as a fraction
		/// of the total screen height (for example, a value of 0.1F
		/// will cause a font rendered at a scale of 1.0F to cover one
		/// tenth of the draw target's height).
		/// </param>
		/// <param name="drawTargetHeight">
		/// The height of the buffer you wish to draw text into,
		/// in pixels.
		/// </param>
		/// <param name="drawTargetDpi">
		/// The DPI of the draw buffer.
		/// </param>
		public void Reset( float virtualScreenLineHeight, int drawTargetHeight, float drawTargetDpi )
		{
			targetHeight = drawTargetHeight;
			screenHeightPerFontScale = virtualScreenLineHeight;
			pointsPerPixel = 72.0F / drawTargetDpi;

			dpiX = dpiY = drawTargetDpi;
		}

		private static Color[] colors = new Color[]
			{
				Color.Red,
				Color.Orange,
				Color.Yellow,
				Color.LightGreen,
				Color.CornflowerBlue,
				Color.Violet,
				Color.Black,
				Color.DarkGray,
				Color.Gray,
				Color.White,
			};

		unsafe int ITextHelper.ProcessFormattingSequence( char* s, int count, FormattingStack format )
		{
			if( count < 2 )
				return 0;

			if( s[0] != '\f' )
				return 0;

			if( s[1] == '-' )
			{
				if( format != null )
					format.Pop();
				return 2;
			}
			if( s[1] == '#' )
			{
				if( count < 8 )
					return 0;

				int r = Helpers.ParseHex( s + 2, 2 );
				if( r < 0 )
					return 0;
				int g = Helpers.ParseHex( s + 4, 2 );
				if( g < 0 )
					return 0;
				int b = Helpers.ParseHex( s + 6, 2 );
				if( b < 0 )
					return 0;

				if( format != null )
					format.PushColor( new Color( (byte)r, (byte)g, (byte)b, format.Color.A ) );

				return 8;
			}
			else if( s[1] >= '0' && s[1] <= '9' )
			{
				if( format != null )
				{
					Color cl = colors[s[1] - '0'];
					format.PushColor( new Color( cl.R, cl.G, cl.B, format.Color.A ) );
				}

				return 2;
			}

			return 0;
		}

		internal static unsafe int DecodeCharacter( char* s, out int charSize )
		{
			//can we have multi-char characters? do we care?
			int ret = (int)s[0];
			charSize = 1;
			return ret;
		}

		float ITextHelper.GetPointSize( float textScale )
		{
			float screenHeight = screenHeightPerFontScale * textScale * targetHeight;
			return screenHeight * pointsPerPixel;
		}

		Vector2 ITextHelper.GetGlyphScale( float textScale, float pointSize )
		{
			float screenHeight = screenHeightPerFontScale * textScale * targetHeight;
			float pointHeight = pointSize / pointsPerPixel;

			float pointScale = screenHeight / pointHeight;
			return new Vector2( pointScale * dpiY / dpiX, pointScale );
		}
	}
}